package com.tsingteng.cosfun.utils;


import java.util.ArrayList;

public class CommandManager {

    /**
     * CommandManager 初始下标
     */
    private static final int DefaultCommandIndex = -1;

    /**
     * CommandManager 当前下标
     */
    private int currentIndex;

    /**
     * CommandManager command数组
     */
    private ArrayList<Command> commands;

    /**
     * canUndo：是否可撤销
     * canRedo：是否可返回撤销
     * 可对此属性做监听，同时把状态绑定到按钮的可点状态上
     */
    private boolean canUndo;
    private boolean canRedo;

    /**
     * 初始化
     */
    public CommandManager() {
        this.currentIndex = DefaultCommandIndex;
        this.setCommands(new ArrayList<Command>());
    }

    /**
     * 添加command
     * @param command 要添加的command
     */
    public void pushCommand(Command command) {
        if (command != null) {
            if (this.check()) {
                try {
                    this.commands.subList(this.currentIndex + 1, this.commands.size() - (this.currentIndex + 1)).clear();
                }catch (IllegalArgumentException e){
                    LogUtils.e("IllegalArgumentException");
                }

            }
            this.currentIndex++;
            this.commands.add(command);
        }
    }

    /**
     * 添加command并且执行
     * @param command 要添加的command
     * @return 返回执行是否成功
     */
    public boolean pushCommandAndApply(Command command) {
        this.pushCommand(command);
        if (command != null) {
            boolean rs = command.apply();
            if (rs) {
                this.setCanUndo(true);
                this.setCanRedo(false);
            }
            return rs;
        }
        return false;
    }

    /**
     * 撤销
     * @return 返回当前执行的command实例
     */
    public Command undo() {
        if (this.isCanUndo()) {
            if (this.checkUndo()) {
                Command command = this.commands.get(this.currentIndex);
                boolean rs = command.unapply();
                if (rs) {
                    this.currentIndex--;
                    this.setCurrentIndex(Math.max(this.currentIndex, DefaultCommandIndex));
                    this.setCanUndo(this.currentIndex != DefaultCommandIndex);
                    this.setCanRedo(true);
                    return command;
                }
            }
        }
        return null;
    }
    /**
     * 返回撤销
     * @return 返回当前执行的command实例
     */
    public Command redo() {
        if (this.isCanRedo()) {
            if (this.checkRedo()) {
                Command command = this.commands.get(this.currentIndex + 1);
                boolean rs = command.apply();
                if (rs) {
                    this.currentIndex++;
                    this.setCurrentIndex(Math.min(this.currentIndex, this.commands.size() - 1));
                    this.setCanUndo(true);
                    this.setCanRedo(this.currentIndex != (this.commands.size() - 1));
                    return command;
                }
            }
        }
        return null;
    }

    /**
     * 校验添加command时是否需要删除当前command后面的comamnds
     * @return 返回是否需要删除
     */
    private boolean check() {
        return this.currentIndex + 1 < this.commands.size();
    }

    /**
     * 校验是否可以撤销
     * @return 返回是否可以撤销
     */
    private boolean checkUndo() {
        return DefaultCommandIndex < this.currentIndex && 0 < this.commands.size();
    }
    /**
     * 校验是否可以返回撤销
     * @return 返回是否可以返回撤销
     */
    private boolean checkRedo() {
        return this.currentIndex < this.commands.size();
    }

    /**
     * 重置
     */
    public void reset() {
        this.commands.clear();
        this.setCurrentIndex(DefaultCommandIndex);
        this.setCanUndo(false);
        this.setCanRedo(false);
    }


    /**
     * getter & setter
     */
    public int getCurrentIndex() {
        return currentIndex;
    }

    public void setCurrentIndex(int currentIndex) {
        this.currentIndex = currentIndex;
    }

    public ArrayList<Command> getCommands() {
        return commands;
    }

    public void setCommands(ArrayList<Command> commands) {
        this.commands = commands;
    }

    public boolean isCanUndo() {
        return canUndo;
    }

    public void setCanUndo(boolean canUndo) {
        this.canUndo = canUndo;
    }

    public boolean isCanRedo() {
        return canRedo;
    }

    public void setCanRedo(boolean canRedo) {
        this.canRedo = canRedo;
    }

}
